import { VantComponent } from '../common/component';
import { isDef } from '../common/validator';

const LONG_PRESS_START_TIME = 600;
const LONG_PRESS_INTERVAL = 200;

// add num and avoid float number
function add(num1: number, num2: number) {
  const cardinal = 10 ** 10;
  return Math.round((num1 + num2) * cardinal) / cardinal;
}

function equal(value1: number | string, value2: number | string) {
  return String(value1) === String(value2);
}

VantComponent({
  field: true,

  classes: ['input-class', 'plus-class', 'minus-class'],

  props: {
    value: {
      type: null,
    },
    integer: {
      type: Boolean,
      observer: 'check',
    },
    disabled: Boolean,
    inputWidth: String,
    buttonSize: String,
    asyncChange: Boolean,
    disableInput: Boolean,
    decimalLength: {
      type: Number,
      value: null as unknown as number,
      observer: 'check',
    },
    min: {
      type: null,
      value: 1,
      observer: 'check',
    },
    max: {
      type: null,
      value: Number.MAX_SAFE_INTEGER,
      observer: 'check',
    },
    step: {
      type: null,
      value: 1,
    },
    showPlus: {
      type: Boolean,
      value: true,
    },
    showMinus: {
      type: Boolean,
      value: true,
    },
    disablePlus: Boolean,
    disableMinus: Boolean,
    longPress: {
      type: Boolean,
      value: true,
    },
    theme: String,
    alwaysEmbed: Boolean,
  },

  data: {
    currentValue: '',
  },

  watch: {
    value() {
      this.observeValue();
    },
  },

  created() {
    this.setData({
      currentValue: this.format(this.data.value).newValue,
    });
  },

  methods: {
    observeValue() {
      const { value } = this.data;
      this.setData({ currentValue: this.format(value).newValue });
    },

    check() {
      const { newValue } = this.format(this.data.currentValue);

      if (!equal(newValue, this.data.currentValue)) {
        this.setData({ currentValue: newValue });
      }
    },

    isDisabled(type: string) {
      const { disabled, disablePlus, disableMinus, currentValue, max, min } =
        this.data;

      if (type === 'plus') {
        return disabled || disablePlus || +currentValue >= +max;
      }

      return disabled || disableMinus || +currentValue <= +min;
    },

    onFocus(event: WechatMiniprogram.InputFocus) {
      this.$emit('focus', event.detail);
    },

    onBlur(event: WechatMiniprogram.InputBlur) {
      const data = this.format(event.detail.value);

      this.setData({ currentValue: data.newValue });

      this.emitChange(data);

      this.$emit('blur', {
        ...event.detail,
        value: +data.newValue,
      });
    },

    // filter illegal characters
    filter(value: string): string {
      value = String(value).replace(/[^0-9.-]/g, '');

      if (this.data.integer && value.indexOf('.') !== -1) {
        value = value.split('.')[0];
      }

      return value;
    },

    format(value: string) {
      // filter illegal characters and format integer
      const safeValue = this.filter(value);

      // format range
      const rangeValue = Math.max(
        Math.min(this.data.max, +safeValue),
        this.data.min
      );

      // format decimal
      const newValue = isDef(this.data.decimalLength)
        ? rangeValue.toFixed(this.data.decimalLength)
        : String(rangeValue);

      return { value, newValue };
    },

    onInput(event: WechatMiniprogram.Input) {
      const { value = '' } = event.detail || {};

      // allow input to be empty
      if (value === '') {
        return;
      }

      const formatted = this.format(value);

      this.emitChange(formatted);
    },

    emitChange(data: { value: string; newValue: string }) {
      const { value, newValue } = data;

      if (!this.data.asyncChange) {
        // fix when input 11. parsed to 11, unable to enter decimal
        this.setData({ currentValue: +value === +newValue ? value : newValue });
      }

      this.$emit('change', +newValue);
    },

    onChange() {
      const { type } = this;

      if (this.isDisabled(type)) {
        this.$emit('overlimit', type);
        return;
      }

      const diff = type === 'minus' ? -this.data.step : +this.data.step;

      const value = this.format(String(add(+this.data.currentValue, diff)));

      this.emitChange(value);
      this.$emit(type);
    },

    longPressStep() {
      this.longPressTimer = setTimeout(() => {
        this.onChange();
        this.longPressStep();
      }, LONG_PRESS_INTERVAL);
    },

    onTap(event: WechatMiniprogram.TouchEvent) {
      const { type } = event.currentTarget.dataset;
      this.type = type;
      this.onChange();
    },

    onTouchStart(event: WechatMiniprogram.TouchEvent) {
      if (!this.data.longPress) {
        return;
      }
      clearTimeout(this.longPressTimer);

      const { type } = event.currentTarget.dataset;
      this.type = type;
      this.isLongPress = false;

      this.longPressTimer = setTimeout(() => {
        this.isLongPress = true;
        this.onChange();
        this.longPressStep();
      }, LONG_PRESS_START_TIME);
    },

    onTouchEnd() {
      if (!this.data.longPress) {
        return;
      }

      clearTimeout(this.longPressTimer);
    },
  },
});
